Re: Generating a true random number?

Bennett Todd (bet@std.sbi.com)
Fri, 3 Jun 1994 16:30:18 -0400 (EDT)

Security applications have a different sense of ``random'' from the
statistical definitions that apply to pseudo-random number generators used
for (e.g.) Monte Carlo simulation.

In a security setting, the heart of the matter is unguessability. I've got a
script I use for generating passwords, and that sort of thing. It shoots for
unguessability. I append it after my .sig.

-Bennett
bet@sbi.com

#!/usr/local/bin/perl
($progname=$0)=~s#.*/##;
$syntax="syntax: $progname [-n] [len [printable|all|hex|HEX]]\n";
require 'getopts.pl';
&Getopts('n') || die $syntax;

defined($max = shift)      || ($max = 8);
defined($alphabet = shift) || ($alphabet = 'printable');
($#ARGV == -1)             || die $syntax;

domain: {
	$_ = $alphabet;
	/^printable$/ && do { $chars = pack("C*", (32 .. 126)); last domain; };
	/^all$/ && do       { $chars = pack("C*", ( 0 .. 255)); last domain; };
	/^hex$/ && do       { $chars = "0123456789abcdef";      last domain; };
	/^HEX$/ && do       { $chars = "0123456789ABCDEF";      last domain; };
	die $syntax;
};

@chars = split('', $chars);

@randstring = split('', &randbits($max));

foreach $i (@randstring) {
	print $chars[ord($i) % $#chars];
}

print "\n" unless $opt_n;

exit 0;

sub randbits {
	local($nbytes) = @_;
	local(*_, $noise, $buf, $limit, $discard, $newlen);

	# Here's the big non-portability. This command works really well on
	# Sun workstations running SunOS; on other platforms, root around for
	# commands that report lots of detailed OS internals state, plus a
	# compressor or other program to smear the bits about.
	$noise = '(ps -agxlww;free;pstat -afipSsT)2>/dev/null|compress';

	# Run the noise command; in case it croaks, slap on whatever other state we
	# can conveniently (portably) find.
	$buf = `$noise` . $$ . getppid() . time . join('', %ENV);

	# Gotta have enough bits for at least one good fold.
	$limit = int(length($buf)/2);
	die "Insufficient random state; try less than $limit\n" if $nbytes > $limit;

	# Get Perl to treat ^ as bit-string op
	$discard = vec($buf, 0, 8);

	# Now fold the noise down by repeated xor, halving the buffer until it's but
	# little bigger than xauth(1) wants.
	while (length($buf) >= $nbytes*2) {
		$newlen = int((length($buf) + 1) / 2);
		$buf = (substr($buf, 0, $newlen) ^ substr($buf, $newlen, $newlen));
	}

	# Final fold may turn in some nulls, but fits it exactly to $nbytes without
	# discarding any bits.
	substr($buf, 0, $nbytes) ^ substr($buf, $nbytes, $nbytes);
}